﻿/*
   +----------------------------------------------------------------------+
   | TABLE EXPORT TOOL                                                    |
   +----------------------------------------------------------------------+
   | Copyright © 2013 - 2018, The Machine Studio.                         |
   +----------------------------------------------------------------------+
   | See https://gitee.com/signorinotang/ServerStudy for last version     | 
   |                                                                      |
   +----------------------------------------------------------------------+
   | Author: Downey <Email: signorinotang@sina.cn>                        |
   |                <QQ: 116297528>                                       |                                                                     |
   +----------------------------------------------------------------------+
*/


using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections;
using NPOI.SS.UserModel;
using NPOI.HSSF.UserModel;
using System.IO;
using System.Data;
using NPOI.XSSF.UserModel;
using System.Xml;
using System.Text.RegularExpressions;
using adc;

namespace tabtool
{
    public class ExcelHelper {
        IWorkbook hssfworkbook;
        public DataTable ImportExcelFile(string filePath)
        {
            try
            {
                using (FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read))
                {
                    hssfworkbook = new XSSFWorkbook(file);
                }
            }
            catch (Exception e)
            {
                throw e;
            }

            ISheet sheet = hssfworkbook.GetSheetAt(0);
            System.Collections.IEnumerator rows = sheet.GetRowEnumerator();

            DataTable dt = new DataTable();
            IRow row0 = sheet.GetRow(0);
            for (int j = row0.FirstCellNum; j < (row0.LastCellNum); j++)
            {
                dt.Columns.Add(row0.GetCell(j).ToString());
            }

            while (rows.MoveNext())
            {
                IRow row = (XSSFRow)rows.Current;
                DataRow dr = dt.NewRow();

                for (int i = 0; i < row.LastCellNum; i++)
                {
                    ICell cell = row.GetCell(i);
                    if (cell == null)
                    {
                        dr[i] = null;
                    }
                    else
                    {
                        dr[i] = cell.ToString();
                    }
                }
                dt.Rows.Add(dr);
            }
            return dt;
        }
        public void ExportXmlFile(string filepath, DataTable dt, int firstrow)
        {
            using (FileStream fs = new FileStream(filepath, FileMode.Create, FileAccess.Write))
            {
                XmlWriterSettings setting = new XmlWriterSettings();
                setting.Indent = true;
                setting.Encoding = new UTF8Encoding(false);
                setting.NewLineChars = Environment.NewLine;

                using (XmlWriter xw = XmlWriter.Create(fs, setting))
                {
                    xw.WriteStartDocument(true);
                    xw.WriteStartElement("datas");
                    for (int i = firstrow; i < dt.Rows.Count; i++)
                    {
                        xw.WriteStartElement("data");
                        for (int j = 0; j < dt.Columns.Count; j++)
                        {
                            xw.WriteAttributeString(dt.Columns[j].ToString(), dt.Rows[i].ItemArray[j].ToString());
                        }
                        xw.WriteEndElement();
                    }
                    xw.WriteEndElement();
                    xw.WriteEndDocument();
                }
            }
        }
        public void ExportTxtFile(string filepath, DataTable dt, int firstrow)
        {
            using (FileStream fs = new FileStream(filepath, FileMode.Create, FileAccess.Write))
            {
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                for (int i = firstrow; i < dt.Rows.Count; i++)
                {
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        if (j == dt.Columns.Count - 1)
                        {
                            sw.WriteLine(dt.Rows[i].ItemArray[j].ToString());
                        }
                        else
                        {
                            sw.Write(dt.Rows[i].ItemArray[j].ToString() + "\t");
                        }
                    }
                }
                sw.Close();
            }
        }
        public bool IsExportFile(string key, DataTable dt)
        {
            return IsExportField(key, dt, 0);
        }
        public bool IsExportField(string key, DataTable dt, int col)
        {
            //默认为都导出 NULL 不导出 CLIENT 导出客户端 SERVER 导出服务端
            string s = dt.Rows[1].ItemArray[col].ToString();
            if (s.Contains("null"))
            {
                return false;
            }
            if (s.Contains(key))
            {
                return true;
            }
            if (!s.Contains("client") && !s.Contains("server"))
            {
                return true;
            }
            return false;
        }
        internal base_type GetElemType(string s) {
            if (s == "int") {
                return new int32_type();
            } else if (s == "int64") {
                return new int64_type();
            } else if (s == "float") {
                return new float32_type();
            } else if (s == "double") {
                return new float64_type();
            } else if (s == "string") {
                return new string_type();
            } else {
                Match reg = null;
                if ((reg = Regex.Match(s, @"(\w+)\<([A-Za-z0-9_.|,<>]+)\>")).Success) {
                    if (reg.Groups[1].Value == "tuple") {
                        var type = new tuple_type();
                        var parts = reg.Groups[2].Value.Split(',');
                        foreach(var p in parts) {
                            type.elem_types.Add(GetElemType(p));
                        }
                        return type;
                    } else if(reg.Groups[1].Value == "list") {
                        var type = new list_type();
                        type.elem_type = GetElemType(reg.Groups[2].Value);
                        return type;
                    }
                } else {
                    var tbs = TableStruct.GetTableMetaByName(s);
                    if (tbs != null) {
                        var type = new struct_type();
                        type.struct_name = s;
                        foreach (var f in tbs.Fields) {
                            type.elem_types.Add(f.type);
                        }
                        return type;
                    }
                }
            }
            throw new Exception("[GET_ELEM_TYPE_ERROR] type: " + s);
        }


        internal base_type GetType(TableField field) {
            if(field.conds[0] == "int") {
                return new int32_type();
            } else if(field.conds[0] == "int64") {
                return new int64_type();
            } else if(field.conds[0] == "float") {
                return new float32_type();
            } else if (field.conds[0] == "double") {
                return new float64_type();
            } else if(field.conds[0] == "string") {
                return new string_type();
            } else if(field.conds[0] == "enum") {
                var type = new enum_type();
                type.table_name = field.conds[1];
                return type;
            } else if(field.conds[0] == "mask") {
                var type = new mask_type();
                type.table_name = field.conds[1];
                return type;
            } else if(field.conds[0] == "datetime") {
                return new date_type();
            } else if(field.conds[0] == "list") {
                var type = new list_type();
                type.elem_type = GetElemType(field.conds[1]);
                return type;
            } else if(field.conds[0] == "tuple") {
                var type = new tuple_type();
                var parts = field.conds[1].Split(',');
                foreach(var p in parts) {
                    type.elem_types.Add(GetElemType(p));
                }
                return type;
            } else {
                var tbs = TableStruct.GetTableMetaByName(field.conds[0]);
                if(tbs != null) {
                    var type = new struct_type();
                    type.struct_name = field.conds[0];
                    foreach(var f in tbs.Fields) {
                        type.elem_types.Add(f.type);
                    }
                    return type;
                }
            }
            throw new Exception("[GET_TYPE_ERROR] type: " + field.conds[0]);
        }




        internal TableMeta ParseTableMeta(string filename, DataTable dt, string cmp)
        {
            TableMeta meta = new TableMeta();
            meta.TableName = filename;
            meta.dt = dt;
            //第一行name 第二行类型和校验条件 第三行注释
            for (int i = 0; i < dt.Columns.Count; i++)
            {
                if (!IsExportField(cmp, dt, i)) { continue; }
                Match reg = null;
                TableField field = new TableField();
                field.fieldName = dt.Rows[0].ItemArray[i].ToString();
                field.fieldDesc = dt.Rows[2].ItemArray[i].ToString();
                string[] conds = dt.Rows[1].ItemArray[i].ToString().Split('=');
                foreach (string s in conds) {
                    string[] without_blank = s.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    if (without_blank.Length != 1)
                    {
                        throw new Exception("[PARSE_TAbLE_META_ERROR] table:" + filename + " condition:" + s + " remove_empty not 1");
                    }
                    if ((reg = Regex.Match(without_blank[0], @"(\w+)\<([A-Za-z0-9_.|,<>]+)\>")).Success)
                    {
                        field.conds.Add(reg.Groups[1].Value);
                        field.conds.Add(reg.Groups[2].Value);
                    }
                    else
                    {
                        field.conds.Add(without_blank[0]);
                    }
                }
                if (i == 0) {
                    if(field.conds[0] == "list") {
                        meta.container_type = TableFieldType.list_type;
                        field.type = GetElemType(field.conds[1]);
                    } else if(field.conds[0] == "map") {
                        meta.container_type = TableFieldType.map_type;
                        string s = field.conds[1];
                        if (s == "int" || s == "int64" || s == "string") {
                            field.type = GetElemType(s);
                        } else {
                            throw new Exception("[PARSE_TAbLE_ERROR] table:" + filename + " field:" + field.fieldName + " unknow map key type" + field.conds[1]);
                        }
                    } else {
                        throw new Exception("[PARSE_TAbLE_ERROR] table:" + filename + " field:" + field.fieldName + " unknow container_type" + field.conds[0]);
                    }
                } else {
                    field.type = GetType(field);
                }
                meta.Fields.Add(field);
            }
            return meta;
        }
        public void ExportTxtFileEx(string filepath, DataTable dt, string key, int[] ignorerows)
        {
            using (FileStream fs = new FileStream(filepath, FileMode.Create, FileAccess.Write))
            {
                StreamWriter sw = new StreamWriter(fs, Encoding.UTF8);
                for (int i = 0; i < dt.Rows.Count; i++)
                {
                    if (ignorerows.Contains(i)) continue;
                    for (int j = 0; j < dt.Columns.Count; j++)
                    {
                        if (!IsExportField(key, dt, j))
                        {
                            if (j == dt.Columns.Count - 1)
                            {
                                sw.Write("\n");
                            }
                            continue;
                        }
                        if (j == dt.Columns.Count - 1)
                        {
                            sw.WriteLine("\t" + dt.Rows[i].ItemArray[j].ToString());
                        }
                        else if (j == 0)
                        {
                            sw.Write(dt.Rows[i].ItemArray[j].ToString());
                        }
                        else
                        {
                            sw.Write("\t" + dt.Rows[i].ItemArray[j].ToString());
                        }
                    }
                }
                sw.Close();
            }
        }

        internal void WriteData(adata.zero_copy_buffer stream, base_type type, string content, TableMeta meta, char sp = '|')
        {
            switch (type.type) {
                case e_type.int_type: {
                        Int32 value = 0;
                        bool ret = Int32.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.int64_type: {
                        Int64 value = 0;
                        bool ret = Int64.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.float_type: {
                        float value = 0;
                        bool ret = float.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.double_type: {
                        double value = 0;
                        bool ret = double.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.string_type: {
                        adata.stream.write(stream, meta.GetStrIndex(content));
                    }
                    break;
                case e_type.list_type: {
                        var parts = content.Split(sp);
                        var count = parts.Count();
                        if (count > 0) {
                            adata.stream.write(stream, count);
                            foreach (var p in parts) {
                                WriteData(stream, ((list_type)type).elem_type, p, meta, ',');
                            }
                        }
                    }
                    break;
                case e_type.tuple_type: {
                        var parts = content.Split(sp);
                        var count = parts.Count();
                        if(count > 0) {
                            if (count != ((tuple_type)type).elem_types.Count()) {
                                throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " TupleType: Elem Count Not Equal !!!");
                            }
                            for(int i = 0; i < count; ++i) {
                                WriteData(stream, ((tuple_type)type).elem_types[i], parts[i], meta);
                            }
                        }
                    }
                    break;
                case e_type.struct_type: {
                        var parts = content.Split(sp);
                        var count = parts.Count();
                        if (count > 0) {
                            if (count != ((struct_type)type).elem_types.Count()) {
                                throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " StructType: Elem Count Not Equal !!!");
                            }
                            for (int i = 0; i < count; ++i) {
                                WriteData(stream, ((struct_type)type).elem_types[i], parts[i], meta);
                            }
                        }
                    }
                    break;
                case e_type.enum_type: {
                        Int32 value = 0;
                        bool ret = Int32.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.mask_type: {
                        Int32 value = 0;
                        bool ret = Int32.TryParse(content, out value);
                        if (!ret) {
                            throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type:" + type + " Content:" + content);
                        }
                        adata.stream.write(stream, value);
                    }
                    break;
                case e_type.date_type: {
                        DateTime dt = Convert.ToDateTime(content);
                        System.DateTime startTime = TimeZone.CurrentTimeZone.ToLocalTime(new System.DateTime(1970, 1, 1)); // 当地时区
                        Int64 timeStamp = (Int64)(dt - startTime).TotalMilliseconds; // 相差毫秒数
                        adata.stream.write(stream, timeStamp);
                    }
                    break; 
                default:
                    throw new Exception("[WRITE_ERROR] Table:" + meta.TableName + " Type Error !!!");
            }
        }
        internal void WriteType(adata.zero_copy_buffer stream, base_type type) {
            adata.stream.write(stream, (int)type.type);
            switch(type.type) {
                case e_type.int_type:
                case e_type.int64_type:
                case e_type.float_type:
                case e_type.double_type:
                case e_type.string_type:
                case e_type.enum_type:
                case e_type.mask_type:
                case e_type.date_type:
                    return;
                case e_type.tuple_type: {
                        tuple_type t = (tuple_type)type;
                        var count = t.elem_types.Count();
                        adata.stream.write(stream, count);
                        foreach (var e in t.elem_types) {
                            WriteType(stream, e);
                        }
                    }
                    return;
                case e_type.struct_type: {
                        struct_type t = (struct_type)type;
                        var count = t.elem_types.Count();
                        adata.stream.write(stream, count);
                        foreach (var e in t.elem_types) {
                            WriteType(stream, e);
                        }
                    }
                    return;
                case e_type.list_type: {
                        list_type l = (list_type)type;
                        WriteType(stream, l.elem_type);
                    }
                    return;
            }
            throw new Exception("[WRITE_TYPE_ERROR] Type:" + type.type);
        }
        internal void FillStrPool(TableMeta meta, base_type type, string content, char sp = '|'){
            switch (type.type) {
                case e_type.string_type: {
                        if (!meta.CheckStrPool(content)) {
                            meta.string_pool.Add(content);
                        }
                    }
                    return;
                case e_type.tuple_type: {
                        var parts = content.Split(sp);//分隔符
                        var t = (tuple_type)type;
                        for (int k = 0; k < parts.Count(); ++k) {
                            FillStrPool(meta, t.elem_types[k], parts[k]);
                        }
                    }          
                    return;
                case e_type.struct_type: {
                        var parts = content.Split(sp);//分隔符
                        var t = (struct_type)type;
                        for (int k = 0; k < parts.Count(); ++k) {
                            FillStrPool(meta, t.elem_types[k], parts[k]);
                        }
                    }
                    return;
                case e_type.list_type: {
                        var parts = content.Split(sp);//分隔符
                        list_type l = (list_type)type;
                        for (int i = 0; i < parts.Count(); ++i) {
                            FillStrPool(meta, l.elem_type, parts[i], ',');
                        }
                    }
                    return;
            }
            //throw new Exception("[WRITE_STR_POOL_ERROR] Type:" + type.type);
        }
        public void ExportAllData(string filepath, List<TableMeta> metas, string key, int[] ignorerows) { 
            using (FileStream fs = new FileStream(filepath + "all_data.adc", FileMode.Create, FileAccess.Write)) {
                adata.zero_copy_buffer stream = new adata.zero_copy_buffer();
                //写入表的数量
                int rec_count = 0;
                foreach (var asc in CodeGen.associate_info) {
                    if (asc[4] == ">") {
                        foreach (var m in metas) {
                            if (m.TableName == asc[2])
                                ++rec_count;
                        }
                    }
                }
                adata.stream.write(stream, metas.Count + rec_count);
                foreach (var meta in metas) {
                    //写入每张表的adc_def
                    //写入容器类型
                    adata.stream.write(stream, (int)meta.container_type);
                    //写入容器名字
                    adata.stream.write(stream, meta.TableName);
                    //写入字段数量
                    adata.stream.write(stream, meta.Fields.Count);
                    //写入字段名和类型
                    foreach (var field in meta.Fields) {
                        adata.stream.write(stream, field.fieldName);
                        WriteType(stream, field.type);
                    }
                    //填充string_pool
                    for (int i = 3; i < meta.dt.Rows.Count; ++i) {
                        for (int j = 0; j < meta.Fields.Count; ++j) {
                            base_type type = meta.Fields[j].type;
                            string content = meta.dt.Rows[i].ItemArray[j].ToString();
                            FillStrPool(meta, type, content);        
                        }
                    }
                    //写入string pool 长度
                    adata.stream.write(stream, meta.string_pool.Count);
                    foreach(var s in meta.string_pool) {
                        adata.stream.write(stream, s);
                    }
                    //写入数据数量
                    int col_count = meta.dt.Columns.Count;
                    int row_count = meta.dt.Rows.Count - 3;
                    int all_count = col_count * row_count;
                    //写入行数
                    adata.stream.write(stream, row_count);
                    //写入数据
                    for (int i = 3; i < meta.dt.Rows.Count; ++i) {
                        for (int j = 0; j < meta.Fields.Count; ++j) {
                            string content = meta.dt.Rows[i].ItemArray[j].ToString();
                            WriteData(stream, meta.Fields[j].type, content, meta);
                        }
                    }
                }
                //写入recombine数据
                foreach(var asc in CodeGen.associate_info) {
                    if (asc[4] == ">") {
                        foreach (var meta in metas) {
                            if(asc[2] == meta.TableName) {
                                //写入每张表的adc_def
                                //写入容器类型
                                adata.stream.write(stream, (int)TableFieldType.map_type);
                                //写入容器名字
                                adata.stream.write(stream, asc[0]);
                                //写入字段数量
                                adata.stream.write(stream, 1);
                                //写入字段名和类型             
                                adata.stream.write(stream, asc[1]);
                                WriteType(stream, meta.Fields[0].type);                              
                                //写入string pool 长度
                                adata.stream.write(stream, 0);
                                //写入数据
                                Dictionary<string, int> keys = new Dictionary<string, int>();
                                for (int i = 3; i < meta.dt.Rows.Count; ++i) {
                                    string content = meta.dt.Rows[i].ItemArray[0].ToString();
                                    keys[content] = 0;                  
                                }
                                //写入行数
                                adata.stream.write(stream, keys.Count);
                                // 写入key数据
                                foreach (var k in keys) {
                                    WriteData(stream, meta.Fields[0].type, k.Key, meta);
                                }
                                break;
                            }
                        }
                    }
                }

                fs.Write(stream.buffer, 0, stream.write_len);
                fs.Close();
            }
        }
    }
}
